// XMLLib version 1
//
// Author
//
//   Thomas Koos (info@muetze1.de)
//   www.muetze1.de
//
// License
//
//   This Source Code Form is subject to the terms of the Mozilla Public
//   License, v. 1.1. If a copy of the MPL was not distributed with this
//   file, You can obtain one at http://mozilla.org/MPL/1.1/.
//
// Description
//
//   String adapter for Windows-1252 coding.
//
// Revision
//
//  $Id: StrAdapterWindows1252.pas 18 2014-04-21 21:28:10Z muetze1@gmail.com $
//

unit StrAdapterWindows1252;

interface

uses
  StrAdapterBase;

  //## "Windows-1252"
type
    // string adapter for en/decoding strings in Windows-1252
  TWindows1252Adapter = class(TBaseStringAdapter)
  public
      // returns the xml header coding string for this adapter
    class function EncodingID: string; override;

      // Converts the Windows-1252 coded AData to ansi string
      // and returns it.
    function Decode(const AData: AnsiString): string; override;

      // Converts the ansi string AData to Windows-1252 coded string
      // and returns it.
    function Encode(const aData: string): AnsiString; override;
  end;

implementation

{ TWindows1252Adapter }

uses
  StrAdapter,
  SysUtils;

type
  TMapEntry = record
    Code1252: Byte;     // Windows-1252 code
    ANSI: integer;      // possible mapping in ANSI (0 for not mappable (default char), -1 for undefined)
    Unicode: Integer;   // unicode mapping (-1 for undefined)
  end;

const
  coWindows1252MapTable: array[0..31] of TMapEntry = (
    (Code1252: $80; ANSI: $00; Unicode: $20ac),  // euro sign
    (Code1252: $81; ANSI: -1;  Unicode: -1),     // undefined
    (Code1252: $82; ANSI: $27; Unicode: $201a),  // single low-9 quotation mark
    (Code1252: $83; ANSI: $66; Unicode: $0192),  // latin small letter F with hook
    (Code1252: $84; ANSI: $22; Unicode: $201e),  // double low-9 quotation mark
    (Code1252: $85; ANSI: $2e; Unicode: $2026),  // horizontal ellipsis
    (Code1252: $86; ANSI: $00; Unicode: $2020),  // dagger
    (Code1252: $87; ANSI: $00; Unicode: $2021),  // double dagger
    (Code1252: $88; ANSI: $5e; Unicode: $02c6),  // modifier letter circumflex accent
    (Code1252: $89; ANSI: $25; Unicode: $2030),  // per mille sign
    (Code1252: $8a; ANSI: $53; Unicode: $0160),  // latin capital letter S with caron
    (Code1252: $8b; ANSI: $3c; Unicode: $2039),  // single left-pointing angle quotation mark
    (Code1252: $8c; ANSI: $4f; Unicode: $0152),  // latin capital ligature Oe
    (Code1252: $8d; ANSI: -1;  Unicode: -1),     // undefined
    (Code1252: $8e; ANSI: $5a; Unicode: $017d),  // latin capital letter Z with caron
    (Code1252: $8f; ANSI: -1;  Unicode: -1),     // undefined
    (Code1252: $90; ANSI: -1;  Unicode: -1),     // undefined
    (Code1252: $91; ANSI: $27; Unicode: $2018),  // left single quotation mark
    (Code1252: $92; ANSI: $27; Unicode: $2019),  // right single quotation mark
    (Code1252: $93; ANSI: $22; Unicode: $201c),  // left double quotation mark
    (Code1252: $94; ANSI: $22; Unicode: $201d),  // right double quotation mark
    (Code1252: $95; ANSI: $2a; Unicode: $2022),  // bullet
    (Code1252: $96; ANSI: $2d; Unicode: $2013),  // En dash
    (Code1252: $97; ANSI: $2d; Unicode: $2014),  // Em dash
    (Code1252: $98; ANSI: $7e; Unicode: $02dc),  // small tilde
    (Code1252: $99; ANSI: $54; Unicode: $2122),  // trade mark sign
    (Code1252: $9a; ANSI: $73; Unicode: $0161),  // latin small letter S with caron
    (Code1252: $9b; ANSI: $3e; Unicode: $203a),  // single right-pointing angle quotation mark
    (Code1252: $9c; ANSI: $6f; Unicode: $0153),  // latin small ligature Oe
    (Code1252: $9d; ANSI: -1;  Unicode: -1),     // undefined
    (Code1252: $9e; ANSI: $7a; Unicode: $017e),  // latin small letter Z with caron
    (Code1252: $9f; ANSI: $59; Unicode: $0178)); // latin capital Let    

  // Description
  //   Convert a String into a Windows-1252 string
  //
  // Params
  //   aData     Original string
  //
  // Returns
  //   The converted string in Windows-1252 coding.
  //
  // Exceptions
  //   ESAIllegalCharacterException
function TWindows1252Adapter.Encode(const aData: string): AnsiString;
var
  i, j: Integer;                  
  lChar: Integer;
  lDataLen: Integer;
  lResult: AnsiString;
  lMatch: Boolean;
begin
  lDataLen := length(aData);

  SetLength(lResult, lDataLen);

  for i := 1 to lDataLen do
  begin
    lChar := Ord(AData[i]);

    if (lChar >= $80) and (lChar <= $9f) then
    begin
      if not fOptions.IgnoreIllegalCharacter then
        raise ESAIllegalCharacterException.CreateFmt('character "%s" can not be saved in Windows-1252 coding!', [AnsiChar(lChar)])
      else
        lChar := ord(Options.DefaultCharacter);
    end
    else if (lChar > 255) then
    begin
      lMatch := false;
      for j := low(coWindows1252MapTable) to high(coWindows1252MapTable) do
      begin
        if coWindows1252MapTable[j].Unicode = lChar then
        begin
          lChar := coWindows1252MapTable[j].Code1252;
          lMatch := true;
          break;
        end;
      end;

      if not lMatch then
      begin
        if not fOptions.IgnoreIllegalCharacter then
          raise ESAIllegalCharacterException.CreateFmt('character "%s" can not be saved in Windows-1252 coding!', [AnsiChar(lChar)])
        else
          lChar := ord(Options.DefaultCharacter);
      end;
    end;

    lResult[i] := AnsiChar(lChar);
  end;

  result := lResult;
end;

  // Description
  //   Convert a Windows-1252 string into a string
  //
  // Params
  //   aData     Original Windows-1252 string
  //
  // Returns
  //   The converted string.
  //
  // Exceptions
  //   ESAIllegalCharacterException
function TWindows1252Adapter.Decode(const AData: AnsiString): String;
var
  i, j: Integer;                  
  lChar: Integer;
  lDataLen: Integer;
  lResult: string;
begin
  lDataLen := length(aData);

  SetLength(lResult, lDataLen);

  for i := 1 to lDataLen do
  begin
    lChar := Ord(AData[i]);

    if (lChar >= $80) and (lChar <= $9f) then
    begin
        // ok, theoretical we can access the table entry directly using
        // lChar - $80 as index. But if the table changes in future versions,
        // I have to think about the needed changes here...
      for j := low(coWindows1252MapTable) to high(coWindows1252MapTable) do
      begin
        if coWindows1252MapTable[j].Code1252 = lChar then
        begin
          if high(Char) > high(AnsiChar) then
            lChar := coWindows1252MapTable[j].Unicode
          else
            lChar := coWindows1252MapTable[j].ANSI;

          if lChar = -1 then
          begin
            if not fOptions.IgnoreIllegalCharacter then
              raise ESAIllegalCharacterException.CreateFmt('illegal character "%s" found!',
                                                           [String(Char(coWindows1252MapTable[j].Code1252))])
            else
              lChar := ord(Options.DefaultCharacter);
          end;
          
          break;
        end;
      end;
    end;

    lResult[i] := chr(lChar);
  end;

  result := lResult;
end;

class function TWindows1252Adapter.EncodingID: string;
begin
  Result := 'Windows-1252';
end;

initialization
    // registering the string adapter to the converter
  TStringAdapter.RegisterAdapter(TWindows1252Adapter);

end.


